package com.study.spring.retry.retryByCustom;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * @author
 * @date 2021年11月16日 22:47
 * @description
 */

@Component
@Aspect
public class RetryAop {


    @Resource
    private ApplicationContext applicationContext;


    @Pointcut("@annotation(com.study.spring.retry.retryByCustom.Retry)")
    public void poincut(){

    }


    @Around(value = "poincut()")
    public Object doBiz(ProceedingJoinPoint point){
        MethodSignature methodSignature  = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        Retry retry = method.getDeclaredAnnotation(Retry.class);
        RetryStrategy retryStrategy = applicationContext.getBean(retry.strategy());


        RetryTask retryTask = new RetryTaskImpl(point);


        retryStrategy.initArgs(retry,retryTask);

        try {
            Object result = point.proceed();

            return result;
        } catch (Throwable throwable) {
            retryStrategy.retryTask();
        }
        return null;
    }


    private class RetryTaskImpl implements RetryTask {

        private ProceedingJoinPoint proceedingJoinPoint;

        private Object result;

        private volatile Boolean asyncRetryState = null;


        public RetryTaskImpl(ProceedingJoinPoint proceedingJoinPoint) {
            this.proceedingJoinPoint = proceedingJoinPoint;
        }


        public void setAsyncRetryState(Boolean asyncRetryState) {
            this.asyncRetryState = asyncRetryState;
        }

        @Override
        public Object getRetryResult() {
            return result;
        }

        @Override
        public Boolean getRetryStatus() {
            return asyncRetryState;
        }

        @Override
        public void setRetrySuccess() {
            this.setAsyncRetryState(true);
        }

        @Override
        public void doTask() throws Throwable {
            this.result = proceedingJoinPoint.proceed();
        }
    }

}

